﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;
using XLua;

//---------------测试类型---------------
public enum Season
{
    spring = 0,
    summer = 1,
    autumn = 2,
    winter= 3,
}
[Serializable]
public class StringAndColor
{
    public string s;
    public Color c;
}
//---------------测试类型---------------

public partial class LuaBehaviour : MonoBehaviour
{
    public interface IObjWrap
    {
        object GetObj();
        void SetObj(object obj);
    }

    public class ObjWrap<T> : IObjWrap
    {
        public T obj;

        public object GetObj()
        {
            return obj;
        }

        public void SetObj(object obj)
        {
            this.obj = (T)obj;
        }
    }

    [Serializable]
    public class SerializedReference
    {
        public string key;
        public UnityEngine.Object obj;
    }

    [Serializable]
    public class SerializedValue
    {
        public string key;
        public string json;
    }

    [HideInInspector]
    public string luaScriptPath;
    [SerializeField][HideInInspector]
    public List<SerializedReference> references;
    [SerializeField][HideInInspector]
    public List<SerializedValue> values;
    public LuaTable LuaInstance;
    private Action<LuaTable> onEnableFunc;
    private Action<LuaTable> onDisableFunc;

    void BindLua()
    {
        LuaInstance = LuaEnvInstance.NewTable();
        //注入自己
        LuaInstance.Set("gameObject", gameObject);
        var rets = LuaEnvInstance.DoString($"return require \"{luaScriptPath}\"");
        var luaClass = (LuaTable)rets[0];
        //注入object类型对象
        foreach (var serializedValue in references)
        {
            //看了下set的代码，没有注册任何object类型的pushfunc,所以最后调用的都是pushany，
            //不用担心类型错误问题
            LuaInstance.Set(serializedValue.key, serializedValue.obj);
        }
        //注入其他类型
        if (values.Count > 0)
        {
            var dicInfos = GetSerializedTypeInfoList(luaClass).ToDictionary((it)=>it.ValueName);
            foreach (var serializedValue in values)
            {
                Type type = dicInfos[serializedValue.key].ValueType;
                LuaInstance.Set(serializedValue.key, JsonToValue(serializedValue.json, type));
            }
        }
        
        var newWith = luaClass.Get<Action<LuaTable, LuaTable>>("newWith");
        newWith(luaClass, LuaInstance);
        this.onEnableFunc = LuaInstance.Get<Action<LuaTable>>("OnEnable");
        this.onDisableFunc = LuaInstance.Get<Action<LuaTable>>("OnDisable");
    }

    void Awake()
    {
        if(LuaInstance == null)
        {
            BindLua();
        }        
        LuaInstance.Get<LuaFunction>("Awake")?.Action(LuaInstance);
    }

    void OnEnable()
    {
        onEnableFunc?.Invoke(LuaInstance);
    }

    void OnDisable()
    {
        onDisableFunc?.Invoke(LuaInstance);
    }

    void OnDestroy()
    {
        onEnableFunc = null;
        onDisableFunc = null;
        LuaInstance.Get<LuaFunction>("OnDestroy")?.Action(LuaInstance);
        LuaInstance = null;
    }
    public class SerializedTypeInfo
    {
        public string ValueName;
        public Type ValueType;
    }
    //运行时和编辑时获取的方式不一样，运行时直接从实例中取即可，编辑时需要起一个虚拟机来加载序列化信息。
    public List<SerializedTypeInfo> GetSerializedTypeInfoList(LuaTable luaClass)
    {
        List<SerializedTypeInfo> infoList = new List<SerializedTypeInfo>();
        var tbl = luaClass.Get<LuaFunction>("GetSerialized").Func<LuaTable,LuaTable>(luaClass);
        tbl.ForEach<string,Type>((name, type)=> infoList.Add(new SerializedTypeInfo() { ValueName = name, ValueType = type }));
        return infoList;
    }

    public static string ValueToJson(object value)
    {
        Type type = value.GetType();
        Type wrapType = typeof(ObjWrap<>).MakeGenericType(type);
        var target = Activator.CreateInstance(wrapType);
        ((IObjWrap)target).SetObj(value);
        return JsonUtility.ToJson(target);
    }

    public static object JsonToValue(string json, Type type)
    {
        if(wrapTypeDict.ContainsKey(type))
        {
            Type wrapType = wrapTypeDict[type];
            var target = JsonUtility.FromJson(json, wrapType);
            return ((IObjWrap)target).GetObj();
        }
        else if(type.IsValueType)
        {
            //Debug.LogError($"msut register {type.FullName} in wrapTypeDict!");
            Debug.LogError($"msut register {type.FullName} in wrapTypeDict, because it is value type!");
            return null;
        }
        else
        {
            Type wrapType = typeof(ObjWrap<>).MakeGenericType(type);
            wrapTypeDict.Add(type, wrapType);
            var target = JsonUtility.FromJson(json, wrapType);
            return ((IObjWrap)target).GetObj();
        }
    }

    //因为值类型泛型的生成在IL2CPP下会有JIT异常，所以值类型必须要注册
    private static Dictionary<Type, Type> wrapTypeDict = new Dictionary<Type, Type>()
    {
        {typeof(Int32), typeof(ObjWrap<Int32>)},
        {typeof(Int64), typeof(ObjWrap<Int64>)},
        {typeof(Single), typeof(ObjWrap<Single>)},
        {typeof(Double), typeof(ObjWrap<Double>)},
        {typeof(Boolean), typeof(ObjWrap<Boolean>)},
        {typeof(String), typeof(ObjWrap<String>)},
        {typeof(Vector2), typeof(ObjWrap<Vector2>)},
        {typeof(Vector3), typeof(ObjWrap<Vector3>)},
        {typeof(Vector4), typeof(ObjWrap<Vector4>)},
        {typeof(Color), typeof(ObjWrap<Color>)},
        {typeof(Rect), typeof(ObjWrap<Rect>)},
        {typeof(Bounds), typeof(ObjWrap<Bounds>)},

        {typeof(List<int>), typeof(ObjWrap<List<int>>)},
        {typeof(List<string>), typeof(ObjWrap<List<string>>)},
        {typeof(List<Vector3>), typeof(ObjWrap<List<Vector3>>)},
        {typeof(List<Color>), typeof(ObjWrap<List<Color>>)},

        //测试类型
        //{typeof(List<StringAndColor>), typeof(ObjWrap<List<StringAndColor>>)},
        {typeof(Season), typeof(ObjWrap<Season>)},
    };
}
